﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using static OpenCvSharp.LineIterator;
using System.Runtime.InteropServices;
using System.Collections.Concurrent;

namespace OpenCvSharp
{
    class VideoBinEncoder
    {
        // 私有静态实例
        private static VideoBinEncoder _instance;


        Boolean forceStop = false;
        int frameStep = 1;

        // 私有构造函数，防止外部实例化
        private VideoBinEncoder()
        {
            
        }

        // 公共静态属性获取单例实例
        public static VideoBinEncoder shareMe()
        {
            if (_instance == null)
            {
                _instance = new VideoBinEncoder();
            }
            return _instance;
        }



        /// <summary>
        /// 停止转换
        /// </summary>
        public void stopConvert()
        {
            forceStop = true;
        }



        ///*******************************************************************************
        /// <summary>
        /// 视频文件转换成灯具的像素bin文件
        /// 2万帧 35秒
        /// </summary>
        /// <param name="selectedFilePath"></param>  文件路径
        /// <param name="stepNum"></param> 每隔多少帧保存一次
        /// <param name="pixels"></param> 灯珠像素点
        /// <param name="progressCallback"></param> 进度回调
        ///*******************************************************************************
        public void ConvertVideoToPixelBin(string selectedFilePath, int stepNum, List<PixelPoint> pixels, Action<int> progressCallback = null)
        {
            int frameIndex = 0;
            int step = 0;
            string binFilePath = "";
            forceStop = false;
            int normalizationSize = LightPixelManager.shareMe().normalizationSize;

            //构建bin文件路径
            try
            {
                // 从路径中提取文件名部分（不含扩展名）
                string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(selectedFilePath);
                // 构建bin文件路径，位于所选文件的同级目录
                binFilePath = Path.Combine(Path.GetDirectoryName(selectedFilePath), "Preview.bin");
            }
            catch
            {
                return;
            }

            //载入视频文件
            VideoCapture cap = new VideoCapture(selectedFilePath);
            if (!cap.IsOpened())
            {
                return;
            }

            // 设置视频捕获参数优化
            cap.Set(VideoCaptureProperties.BufferSize, 2);

            // 创建文件流用于写入二进制数据
            using (FileStream fs = new FileStream(binFilePath, FileMode.Create, FileAccess.ReadWrite))
            using (BinaryWriter writer = new BinaryWriter(fs))
            {
                // 预留头部空间
                ushort headerLength = (ushort)(2 + 1 + 2 + 2 + 2 + 1 + 1 + LightPixelManager.shareMe().chpixels.Count());
                byte[] placeholderHeader = new byte[headerLength];
                writer.Write(placeholderHeader);

                Mat frame = new Mat();
                Mat resizedFrame = new Mat();

                byte[] rgbBuffer = new byte[pixels.Count * 3];

                //播放视频并处理每一帧
                while (!forceStop)
                {
                    try
                    {
                        // 只抓取不解码
                        if (!cap.Grab())
                        {
                            break;
                        }

                        if (step == 0)
                        {
                            // 解码当前帧
                            if (!cap.Retrieve(frame))
                            {
                                break;
                            }

                            // 创建缩放图像
                            Cv2.Resize(frame, resizedFrame, new Size(normalizationSize, normalizationSize), 0, 0, InterpolationFlags.Nearest);
                            ExtractAndSaveRGBData(resizedFrame, writer, pixels, rgbBuffer);

                            frameIndex++;
                            if (frameIndex % 50 == 0 && progressCallback != null)
                            {
                                progressCallback(frameIndex);
                            }
                        }
                        //每隔开多少帧保存一次
                        step++;
                        if (step > stepNum)
                        {
                            step = 0;
                        }
                    }
                    catch (Exception ex)
                    {
                        break;
                    }
                }

                // 处理完成，现在回写正确的头部信息
                ushort cycleTime = (ushort)((stepNum+1) * 33);
                WriteCorrectHeader(fs, frameIndex, cycleTime, LightPixelManager.shareMe().chpixels);
                progressCallback?.Invoke(frameIndex);
            }
        }

        /// <summary>
        /// 并行处理像素数据（高性能版本）
        /// </summary>
        //private void ExtractAndSaveRGBData(Mat frame, BinaryWriter writer, List<PixelPoint> pixels)
        //{
        //    byte[] rgbBuffer = new byte[pixels.Count * 3];

        //    // 使用更高效的并行选项
        //    var options = new ParallelOptions
        //    {
        //        MaxDegreeOfParallelism = Environment.ProcessorCount // 根据CPU核心数调整
        //    };

        //    // 如果 pixels 里的坐标已经确保在范围内，可以移除 try-catch
        //    Parallel.For(0, pixels.Count, options, i =>
        //    {
        //        PixelPoint pixel = pixels[i];
        //        int bufferIndex = i * 3;
        //        Vec3b pixelValue = frame.At<Vec3b>(pixel.Y, pixel.X);
        //        rgbBuffer[bufferIndex] = pixelValue.Item2;     // R
        //        rgbBuffer[bufferIndex + 1] = pixelValue.Item1; // G
        //        rgbBuffer[bufferIndex + 2] = pixelValue.Item0; // B
        //    });

        //    writer.Write(rgbBuffer);
        //}



        private unsafe void ExtractAndSaveRGBData(Mat frame, BinaryWriter writer, List<PixelPoint> pixels, byte[] rgbBuffer)
        {
            byte* framePtr = (byte*)frame.Data;
            int frameStep = (int)frame.Step();

            var partitioner = Partitioner.Create(0, pixels.Count);
            Parallel.ForEach(partitioner, range =>
            {
                for (int i = range.Item1; i < range.Item2; i++)
                {
                    var p = pixels[i];
                    int bufferIndex = i * 3;
                    byte* pixelPtr = framePtr + p.Y * frameStep + p.X * 3;

                    // BGR -> RGB
                    rgbBuffer[bufferIndex] = pixelPtr[2]; // R
                    rgbBuffer[bufferIndex + 1] = pixelPtr[1]; // G
                    rgbBuffer[bufferIndex + 2] = pixelPtr[0]; // B
                }
            });

            writer.Write(rgbBuffer);
        }




        /// <summary>
        ///  写入正确的头部信息
        /// </summary>
        /// <param name="stream"></param> 要写的文件流
        /// <param name="frameCount"></param> 总的帧数
        /// <param name="cycleTime"></param> 每帧间隔时间 ：1秒30帧，33ms
        /// <param name="loopPixelCounts"></param> 回路像素信息
        private void WriteCorrectHeader(FileStream stream, int frameCount, ushort cycleTime, byte[] loopPixelCounts)
        {
            try
            {
                byte dataFormat = 0;                        // 数据格式：0表示RGB像素数据
                ushort frameNum = (ushort)frameCount;       // 帧数
                ushort fadeTime = cycleTime;                       // 渐变时长：默认同周期时长
                byte deviceCount = 1;                       // 设备数量：假设只有1个设备
                byte loopCount = (byte)LightPixelManager.shareMe().chpixels.Count();    // 回路数/设备：假设每个设备1个回

                // 计算头部长度（长度不包含自身所以比前面少2个）
                ushort headerLength = (ushort)(1 + 2 + 2 + 2 + 1 + 1 + loopPixelCounts.Length);

                // 构建完整的头部数据
                using (MemoryStream ms = new MemoryStream())
                using (BinaryWriter headerWriter = new BinaryWriter(ms))
                {
                    // 写入高位在前的ushort值
                    headerWriter.Write(GetBigEndianBytes(headerLength));    // 数据总长度
                    headerWriter.Write(dataFormat);                         // 数据格式 0:RGB
                    headerWriter.Write(GetBigEndianBytes(cycleTime));       // 周期时长 33ms
                    headerWriter.Write(GetBigEndianBytes(frameNum));        // 帧数
                    headerWriter.Write(GetBigEndianBytes(fadeTime));        // 渐变时长 33ms
                    headerWriter.Write(deviceCount);
                    headerWriter.Write(loopCount);

                    // 写入每个回路的像素点数量
                    foreach (byte count in loopPixelCounts)
                    {
                        headerWriter.Write(count);
                    }

                    // 将流位置移到文件开头
                    stream.Seek(0, SeekOrigin.Begin);

                    // 写入正确的头部数据
                    byte[] headerData = ms.ToArray();
                    stream.Write(headerData, 0, headerData.Length);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("写入头部信息时出错: " + ex.Message);
            }
        }


        /// <summary>
        /// 将ushort值转换为高位在前的字节数组
        /// </summary>
        /// <param name="value">ushort值</param>
        /// <returns>高位在前的字节数组</returns>
        private byte[] GetBigEndianBytes(ushort value)
        {
            byte[] bytes = BitConverter.GetBytes(value);
            // 如果系统是小端序，则反转字节顺序
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(bytes);
            }
            return bytes;
        }


        ///*******************************************************************************
        /// <summary>
        ///  将视频转换成图片序列
        ///  2万帧 179秒
        /// </summary>
        ///*******************************************************************************
        public void ConvertVideoToFramePictures(string selectedFilePath, int stepNum, List<PixelPoint> pixels, Action<int> progressCallback = null)
        {
            int frameIndex = 0;
            int step = 0;
            string newPath = "";
            forceStop = false;
            int normalizationSize = LightPixelManager.shareMe().normalizationSize;

            // 记录开始时间
            DateTime startTime = DateTime.Now;

            //创建文件夹
            try
            {
                // 从路径中提取文件名部分（不含扩展名）
                string folderName = Path.GetFileNameWithoutExtension(selectedFilePath);
                // 构建新文件夹的路径，位于所选文件的同级目录
                newPath = Path.Combine(Path.GetDirectoryName(selectedFilePath), folderName);
                // 创建新文件夹
                Directory.CreateDirectory(newPath);
            }
            catch
            {
                return;
            }


            //载入视频文件
            VideoCapture cap = new VideoCapture(selectedFilePath);   // 读取本地视频文件
            if (!cap.IsOpened())
            {
                return;
            }

            Mat frame = new Mat();
            Mat resizedFrame = new Mat();

            //播放视频截图图片
            while (!forceStop)
            {
                try
                {
                    if (cap.Read(frame))    // 抓取和解码，返回下一帧
                    {
                        if (step == 0)
                        {
                            Cv2.Resize(frame, resizedFrame, new Size(normalizationSize, normalizationSize), 0, 0, InterpolationFlags.Nearest);
                            Cv2.ImWrite(newPath + "\\" + frameIndex.ToString("D3") + ".jpg", resizedFrame);
                            //Cv2.ImWrite(newPath + "\\" + frameIndex.ToString("D3") + ".jpg", frame);

                            frameIndex++;
                            if (frameIndex % 10 == 0 && progressCallback != null)
                            {
                                progressCallback(frameIndex);
                                Application.DoEvents();
                            }
                        }
                        //每隔开多少帧保存一次
                        step++;
                        if (step > stepNum)
                        {
                            step = 0;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
                catch
                {
                    MessageBox.Show("发生错误退出！", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    break;
                }
            }


            // 计算处理时间
            TimeSpan processingTime = DateTime.Now - startTime;

            // 处理完成，现在回写正确的头部信息
            progressCallback?.Invoke(frameIndex);

            // 显示处理完成信息，包括处理时间和帧数
            string message = $"操作已完成！\n共处理 {frameIndex} 帧\n处理时间: {processingTime.TotalSeconds:F2} 秒";
            MessageBox.Show(message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);

        }



        ///*******************************************************************************
        /// <summary>
        /// 整理图片文件夹，按数字顺序重命名并移动到new子目录
        /// </summary>
        ///*******************************************************************************
        public void SortAndNumberImages(string selectedFolderPath, Action<int> progressCallback = null)
        {
            List<string> imageFiles = Directory.GetFiles(selectedFolderPath, "*.png", SearchOption.TopDirectoryOnly)
                                              .Concat(Directory.GetFiles(selectedFolderPath, "*.jpg", SearchOption.TopDirectoryOnly))
                                              .Concat(Directory.GetFiles(selectedFolderPath, "*.jpeg", SearchOption.TopDirectoryOnly))
                                              .ToList();

            // 自定义排序规则，按照文件名中的数字进行排序
            var sortedFiles = imageFiles.OrderBy(file => GetNumericPartOfFile(file));



            // 检查“new”子目录是否存在，如果不存在则创建
            string newFolderPath = Path.Combine(selectedFolderPath, "new");
            if (!Directory.Exists(newFolderPath))
            {
                Directory.CreateDirectory(newFolderPath);
            }

            //重命名文件
            int i = 0;
            foreach (string sortedFilePath in sortedFiles)
            {
                string oldFilePath = sortedFilePath;
                string extension = Path.GetExtension(oldFilePath);
                string newFileName = $"{i}{extension}";
                string newFilePath = Path.Combine(Path.GetDirectoryName(oldFilePath), "new", newFileName);

                try
                {
                    File.Move(oldFilePath, newFilePath);
                    Console.WriteLine($"File {oldFilePath} has been renamed to {newFileName}");
                }
                catch (IOException ex)
                {
                    MessageBox.Show($"无法重命名文件：{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                i++;

                // 计算并报告进度
                if (progressCallback != null)
                {
                    progressCallback(i);
                }
            }

            MessageBox.Show("操作已完成！", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        // 提取文件名中的数字部分
        private int GetNumericPartOfFile(string filePath)
        {
            string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePath);
            return int.Parse(fileNameWithoutExtension);
        }

    }
}
